'use strict';

layui.define(['setter', 'jquery', 'layer', 'laytpl', 'util'], function(exports) {
  var $ = layui.jquery;
  var layer = layui.layer;
  var laytpl = layui.laytpl;
  var setter = layui.setter;
  var hint = layui.hint();

  var MODULE_NAME = 'view';

  var ClassName = {
    LOADING_PARENT_RELATIVE: 'layadmin-loading-parent-relative',
    LOADING_PARENT_HIDDEN  : 'layadmin-loading-parent-hidden'
  };

  var Tpl = function(id) {
    this.id = id;
    this.container = $(id || setter.app);
  };

  var view = function(selector) {
    return new Tpl(selector);
  };

  // 加载
  view.loading = function(elem) {
    this.$elem = $(elem);
    var position = this.$elem.css('position');
    if (!position || position === 'static') {
      this.$elem
        .addClass(ClassName.LOADING_PARENT_RELATIVE)
        .addClass(ClassName.LOADING_PARENT_HIDDEN);
    }

    $(elem).append(
      this.$elem.$loading = $('<i class="layui-anim layui-anim-rotate layui-anim-loop layui-icon layui-icon-loading layadmin-loading"></i>')
    );
  };

  // 移除加载
  view.removeLoading = function(elem) {
    var $elem = $(elem);
    if ($elem.length <= 0) {
      $elem = this.$elem;
    }
    $elem && $elem.$loading && $elem.$loading.remove();
    $elem && $elem
      .removeClass(ClassName.LOADING_PARENT_HIDDEN)
      .removeClass(ClassName.LOADING_PARENT_RELATIVE);
  };

  // 弹窗，用于页面展示
  view.popup = function(options) {
    var hasProp       = function(prop) { return $.isPlainObject(options) && (prop in options) };
    var hasPropTitle  = hasProp('title');
    var hasPropAnim   = hasProp('anim');
    var hasPropMaxMin = hasProp('maxmin');
    var hasCloseBtn   = hasProp('closeBtn');
    var optCloseBtn   = hasCloseBtn ? options.closeBtn : true;
    var optSuccess    = options.success;
    var optSkin       = options.skin;
    var optTitle      = hasPropTitle ? options.title : '提示';
    var optAnim       = hasPropAnim ? options.anim : -10;
    var optMaxmin     = hasPropMaxMin ? options.maxmin : (hasPropTitle && Boolean(optTitle));
    var optRestore    = options.restore;
    var optFull       = options.full;

    // 注入页面放大放小的补丁
    var handlerFullPatch = function($layer) {
      if (this.type === 1 && optMaxmin === true) {
        var $container  = $layer.children('.layui-layer-content');
        var isFull      = $layer.find('.layui-layer-setwin>.layui-layer-maxmin')[0];
        $container.height(isFull ? ($container.height() - 20) : 'auto');
      }
    };

    delete options.maxmin;
    delete options.closeBtn;
    delete options.success;
    delete options.skin;
    delete options.title;
    delete options.anim;
    delete options.min;
    delete options.restore;
    delete options.full;

    return layer.open($.extend({
      maxmin    : optMaxmin,
      anim      : optAnim,
      move      : false,
      type      : 1,
      title     : $.type(optTitle) === 'string' ? optTitle : !!options.title,
      content   : $.type(options) === 'string' ? options : '',
      id        : 'layadmin-view-popup',
      skin      : 'layadmin-view-popup' + (optAnim > -10 ? '' : ' layui-anim layui-anim-SlideDown') + (optSkin ? ' ' + optSkin : ''),
      shadeClose: true,
      closeBtn  : false,
      offset    : 't',
      area      : '290px',
      resize    : true,
      full      : function(layero) {
        var area = layero.attr('area');
        var areas = area && area.split(',');
        if (areas && areas.length > 2) {
          areas[2] = (+areas[2]) - 40;
          layero.attr('layadmin-area', areas.join(','));
          layero.attr('area', areas.join(','));
        }

        layero.css('margin-top', '-40px');
        layero.css('border-radius', 0);
        handlerFullPatch.call(this, layero);
        $.isFunction(optFull) && optFull(layero);
      },
      restore: function(layero) {
        layero.css('margin-top', '');
        layero.css('border-radius', '');
        handlerFullPatch.call(this, layero);
        $.isFunction(optRestore) && optRestore(layero);
      },
      success: function(layero, index) {
        if (optTitle && optCloseBtn) {
          var $btnClose = $('<i class="layui-icon" close>&#x1006;</i>');
          layero.prepend($btnClose);
          $btnClose.on('click', function() {
            layer.close(index);
          });
        }

        if (optMaxmin && !options.move) {
          var $setwin = layero.find('.layui-layer-setwin');
          var $max = $setwin.children('.layui-layer-max');
          var $title = layero.find('.layui-layer-title');
          $setwin.children('.layui-layer-min').remove(); // layer 最小化仍有问题，移除这个参数的配置
          $max.hide();
          $title.dblclick(function() {
            $max.trigger('click');
          });
        }

        $.isFunction(optSuccess) && optSuccess.apply(this, arguments);
      }
    }, $.type(options) === 'string' ? {} : options));
  };

  // 弹窗，用于询问
  view.confirm = function(options) {
    var optSuccess = options.success;
    var optBtn = $.isPlainObject(options) && ('btn' in options) ? options.btn : ['确认', '取消'];
    var optYes = options.yes;
    var optCancel = options.cancel;

    delete options.success;
    delete options.btn;
    delete options.yes;
    delete options.cancel;

    optBtn = $.isArray(optBtn) ? optBtn : [optBtn];

    return view.popup($.extend({
      id        : 'layadmin-view-popup-confirm',
      content   : $.type(options) === 'string' ? options : '',
      resize    : false,
      move      : false,
      shadeClose: false,
      maxmin    : false,
      success   : function(layero, index) {
        var $btn;
        var $container = $('<div class="layadmin-view-popup-btn-container"></div>');
        var i = 0;
        var len = optBtn.length;
        var othis = this;

        var createHandler = function(fn) {
          return function() {
            if ($.isFunction(fn)) {
              var ret = fn.apply(othis, [index, layero]);
              ret === false ? '' : layer.close(index);
            } else {
              layer.close(index);
            }
          };
        };

        for (;i < len; i++) {
          if (i === 0) {
            $btn = $('<button class="layui-btn layadmin-btn-confirm">' + optBtn[i] + '</button>');
            $btn.on('click', createHandler(optYes));
          } else if (i === 1) {
            $btn = $('<button class="layui-btn layadmin-btn-cancel">' + optBtn[i] + '</button>');
            $btn.on('click', createHandler(optCancel));
          } else {
            $btn = $('<button class="layui-btn layui-layer-btn' + i + '">' + optBtn[i] + '</button>');
            $btn.on('click', createHandler(options['btn' + (i + 1)]));
          }
          $container.append($btn);
        }
        len && layero.append($container);

        $.isFunction(optSuccess) && optSuccess.apply(this, arguments);
      }
    }, $.type(options) === 'string' ? {} : options));
  };

  // 弹窗，用于警示
  view.alert = function(options, type) {
    var types = {
      success: '<i class="layui-icon layui-icon-ok"></i>',
      error  : '<i class="layui-icon layui-icon-close"></i>',
      info   : 'i',
      warning: '!'
    };

    var optSuccess = options.success;
    var optBtn = $.isPlainObject(options) && ('btn' in options) ? options.btn : (function() {
      return type === 'error' || type === 'warning' ? ['确认', '取消'] : ['确认'];
    }());

    delete options.success;
    delete options.btn;

    return view.confirm($.extend({
      id     : 'layadmin-view-popup-alert',
      btn    : optBtn,
      content: $.type(options) === 'string' ? options : '',
      success: function(layero) {
        if (type) {
          var $icon = $('<span class="layadmin-view-popup-status is-' + type + '">' + types[type] + '</span>');
          layero.find('#' + this.id).addClass('layadmin-layer-content').prepend($icon);
        }
        $.isFunction(optSuccess) && optSuccess.apply(this, arguments);
      }
    }, $.type(options) === 'string' ? {} : options));
  };

  // 弹窗，用于错误
  view.error = function(message) {
    return view.alert({
      id        : 'layadmin-view-popup-error',
      content   : message,
      title     : false,
      btn       : [],
      maxWidth  : 300,
      shadeClose: true,
      anim      : 6,
      maxmin    : false
    }, 'warning');
  };

  // 弹窗，用于广播
  view.broadcast = function(options) {
    var optTitle = options.title || '通知';

    delete options.title;
    delete options.resize;
    delete options.maxmin;
    delete options.move;
    delete options.anim;

    return view.popup($.extend({
      title  : optTitle,
      id     : 'layadmin-view-popup-broadcast',
      content: $.type(options) === 'string' ? options : '',
      anim   : -2,
      skin   : 'layadmin-view-popup-broadcast layui-anim layui-anim-Broadcast',
      offset : 'rb',
      shade  : false,
      resize : false,
      move   : false,
      time   : 5 * 1000
    }, $.type(options) === 'string' ? {} : options));
  };

  // 弹窗，抽屉
  view.popupRight = function(options) {
    delete options.offset;
    delete options.anim;
    delete options.move;
    delete options.resize;

    return view.popup($.extend({
      id      : 'layadmin-view-popup-popupright',
      content : $.type(options) === 'string' ? options : '',
      closeBtn: false,
      title   : false,
      area    : '300px',
      anim    : -2,
      offset  : 'r',
      skin    : 'layadmin-view-popup-popupright layui-anim layui-anim-PopupRight',
      resize  : false,
      move    : false
    }, options));
  };

  // 绑定页面事件
  view.on = view.onevent = function(events, type) {
    $.isPlainObject(events) && layui.util.event('lay-event', events, type || 'click');
  };

  Tpl.prototype.render = function render(path, params) {
    var that = this;

    path = setter.views + (path.slice(0, 1) === '/' ? path.slice(1) : path) + setter.engine;
    view.loading(that.container);

    $.ajax({
      url     : path,
      type    : 'get',
      dataType: 'html',
      data    : {
        v: layui.cache.version
      },
      success: function(html) {
        html = '<div>' + html + '</div>';

        var $title = $(html).find('title');
        var title = $title.text() || (html.match(/\<title\>([\s\S]*)\<\/title>/) || [])[1];

        var res = {
          title: title,
          body : html
        };

        that.params = params || {};

        if (that.then) {
          that.then(res);
          delete that.then;
        }

        that.parse(html);
        view.removeLoading();

        if (that.done) {
          that.done(res);
          delete that.done;
        }
      },
      error: function(e) {
        view.removeLoading();

        if (that.render.isError) {
          return view.error('请求视图文件异常，状态：' + e.status);
        }

        if (e.status === 404) {
          that.render('template/tips/404');
        } else {
          that.render('template/tips/error');
        }

        that.render.isError = true;
      }
    });

    return that;
  };

  // 解析模板
  Tpl.prototype.parse = function(html, refresh, callback) {
    var that = this;
    var isScriptTpl = typeof html === 'object'; // 是否模板元素
    var elem = isScriptTpl ? html : $(html);
    var elemTemp = isScriptTpl ? html : elem.find('*[template]');

    var fn = function(options) {
      var tpl = laytpl(options.dataElem.html());

      var res = $.extend({
        params: router.params
      }, options.res);

      options.dataElem.after(tpl.render(res));
      typeof callback === 'function' && callback();

      try {
        options.done && new Function('d', options.done)(res);
      } catch (e) {
        console.error(options.dataElem[0], '\n存在错误回调脚本\n\n', e);
      }
    };

    var router = layui.router();

    elem.find('title').remove();
    that.container[refresh ? 'after' : 'html'](elem.children());

    router.params = that.params || {};

    // 遍历模板区块
    for (var i = elemTemp.length; i > 0; i--) {
      (function() {
        var dataElem = elemTemp.eq(i - 1);
        var layDone = dataElem.attr('lay-done') || dataElem.attr('lay-then'); // 获取回调
        var url = laytpl(dataElem.attr('lay-url') || '').render(router);// 接口 url
        var data = laytpl(dataElem.attr('lay-data') || '').render(router);// 接口参数
        var headers = laytpl(dataElem.attr('lay-headers') || '').render(router); // 接口请求的头信息

        try {
          data = new Function('return ' + data + ';')();
        } catch (e) {
          hint.error('lay-data: ' + e.message);
          data = {};
        }

        try {
          headers = new Function('return ' + headers + ';')();
        } catch (e) {
          hint.error('lay-headers: ' + e.message);
          headers = headers || {};
        }

        if (url) {
          view.req({
            type    : dataElem.attr('lay-type') || 'get',
            url     : url,
            data    : data,
            dataType: 'json',
            headers : headers,
            success : function(res) {
              fn({
                dataElem: dataElem,
                res     : res,
                done    : layDone
              });
            }
          });
        } else {
          fn({
            dataElem: dataElem,
            done    : layDone
          });
        }
      }());
    }

    return that;
  };

  // 自动渲染数据模板
  Tpl.prototype.autoRender = function(id, callback) {
    var that = this;
    $(id || 'body').find('*[template]').each(function(index, item) {
      var othis = $(this);
      that.container = othis;
      that.parse(othis, 'refresh');
    });
  };

  // 直接渲染字符
  Tpl.prototype.send = function(views, data) {
    var tpl = laytpl(views || this.container.html()).render(data || {});
    this.container.html(tpl);
    return this;
  };

  // 刷新试图
  Tpl.prototype.refresh = function(callback) {
    var that = this;
    var next = that.container.next();
    var templateid = next.attr('lay-templateid');

    // eslint-disable-next-line eqeqeq
    if (that.id != templateid) return that;

    that.parse(that.container, 'refresh', function() {
      that.$container.siblings('[lay-templateid="' + that.id + '"]:last').remove();
      typeof callback === 'function' && callback();
    });

    return that;
  };

  // 请求成功后回调
  Tpl.prototype.then = function then(callback) {
    this.then = callback;
    return this;
  };

  // 渲染完后回调
  Tpl.prototype.done = function done(callback) {
    this.done = callback;
    return this;
  };

  exports(MODULE_NAME, view);
});
